package api

import (
	"github.com/alecthomas/log4go"
	"moss/conf"
	"moss/fs"
	"moss/meta"
	"moss/proto"
	"moss/status"
	"net/http"
	"os"
	"time"
)

func PutBucketLogging(requestId string, accessKeyId string, bucketName string, targetBucket string,
	targetPrefix string, location string) status.Status {

	if !validIdentifier(bucketName) {
		return status.InvalidBucketName
	}

	sourceBucketMeta, ok := meta.GetBucketInfo(bucketName)
	if !ok {
		return status.NoSuchBucket
	}
	if sourceBucketMeta.OwnerAccessKeyId != accessKeyId {
		return status.AccessDenied
	}

	targetBucketMeta, ok := meta.GetBucketInfo(targetBucket)
	if !ok {
		return status.InvalidTargetBucketForLogging
	}
	if targetBucketMeta.OwnerAccessKeyId != accessKeyId {
		return status.AccessDenied
	}

	if targetBucketMeta.Location != sourceBucketMeta.Location {
		return status.InvalidTargetBucketForLogging
	}

	meta.SetLogging(bucketName, accessKeyId, targetBucket, targetPrefix)

	return status.Success
}

func GetBucketLogging(requestId string, accessKeyId string, bucketName string) (*proto.BucketLoggingStatus, status.Status) {
	if !validIdentifier(bucketName) {
		return nil, status.InvalidBucketName
	}

	bucketMeta, ok := meta.GetBucketInfo(bucketName)
	if !ok {
		return nil, status.NoSuchBucket
	}

	if bucketMeta.OwnerAccessKeyId != accessKeyId {
		return nil, status.AccessDenied
	}

	var xmlDoc proto.BucketLoggingStatus
	bucketLogging, ok := meta.GetLogging(bucketName)

	if ok {
		xmlDoc.LoggingEnabled.TargetBucket = bucketLogging.TargetBucket
		xmlDoc.LoggingEnabled.TargetPrefix = bucketLogging.TargetPrefix
	}

	return &xmlDoc, status.Success
}

func DeleteBucketLogging(requestId string, accessKeyId string, bucketName string) status.Status {
	if !validIdentifier(bucketName) {
		return status.InvalidBucketName
	}

	bucketMeta, ok := meta.GetBucketInfo(bucketName)
	if !ok {
		return status.NoSuchBucket
	}

	if bucketMeta.OwnerAccessKeyId != accessKeyId {
		return status.AccessDenied
	}

	bucketList, ok := meta.GetLogging(bucketName)
	if !ok {
		return status.NoLogging
	}

	meta.DeleteLogging(bucketList.BucketName)
	return status.Success
}

func WriteLogging(req *http.Request, requestId string, apiName string, rc status.Status,
	bucketName, objectName, accessKeyId string, startEpoch string, processDelay int) {

	_, ok := meta.GetLogging(bucketName)
	if !ok {
		return
	}

	var object *meta.Object
	var size int64
	var userID string
	var userSize int

	if objectName != "" {
		object, _ = meta.GetObjectInfo(bucketName, objectName)
		size = object.Size
		userID = object.UserMeta
		userSize = getSizeOfUserMeta(object.UserMeta)
	} else {
		size = 0
		userID = ""
	}

	gLogger := make(log4go.Logger)

	gLogger.AddFilter(bucketName+"_log.log", log4go.INFO, log4go.NewFileLogWriter(bucketName+"_log.log", false))
	gLogger.Info("---------------------------------")
	gLogger.Info("Remote IP: %s", req.RemoteAddr)
	gLogger.Info("Reserved")
	gLogger.Info("Reserved")
	gLogger.Info("Time:  %v", startEpoch)
	gLogger.Info("Request-URI:  %v", req.RequestURI)
	gLogger.Info("HTTP Status:  %s", status.GetCodeString(rc))
	gLogger.Info("SentBytes:  %d", size)
	gLogger.Info("RequestTime (ms):  %d", processDelay)
	gLogger.Info("Referer:  %s", req.Referer())
	gLogger.Info("User-Agent:  %v", req.UserAgent())
	gLogger.Info("HostName:  %s", req.Host)
	gLogger.Info("Request ID:  %s", requestId)
	gLogger.Info("LoggingFlag:  %v", true)
	gLogger.Info("Requester ID:  %s", accessKeyId)
	gLogger.Info("Operation:  %s", apiName)
	gLogger.Info("Bucket:  %s", bucketName)
	gLogger.Info("Key:  %s", objectName)
	gLogger.Info("ObjectSize:  %d", size)
	gLogger.Info("Server Cost Time (ms):  ")
	gLogger.Info("Error Code:  %s", status.GetMessage(rc))
	gLogger.Info("Request Length:  %d", userSize)
	gLogger.Info("UserID:  %s", userID)
	gLogger.Info("Delta DataSize:  ")
	gLogger.Info("Sync Request:  ")
	gLogger.Info("Reserved  ")
}

func generateLoggingName(targetPrefix string, bucketName string) string {
	return targetPrefix + "-" + bucketName + "-" + time.Now().Format(time.RFC3339)
}

func UpdateLoggingRoutine() {
	loggingLists, _ := meta.GetLoggingList()
	for _, entry := range loggingLists {
		go func() {
			bucketName := entry.BucketName
			objectName := generateLoggingName(entry.TargetPrefix, bucketName)

			fileInfo, err := os.Stat(bucketName + "_log.log")
			if os.IsNotExist(err) {
				os.Create(bucketName + "_log.log")
				fileInfo, _ = os.Stat(bucketName + "_log.log")
			}
			fileSize := fileInfo.Size()

			fileLocation := fs.GetObjectLocation(bucketName, objectName, 0)
			f, err := os.Open(bucketName + "_log.log")

			rc := fs.WriteFile(fileLocation, f, fileInfo.Size(), "")
			if rc != status.Success {
				return
			}

			tag := fs.CalculateMD5(fileLocation, "")
			if len(tag) == 0 {
				return
			}

			crcChecksum := fs.CalculateCRC64(fileLocation)
			if len(crcChecksum) == 0 {
				return
			}

			meta.SetObjectInfo(entry.TargetBucket, objectName, entry.OwnerAccessKeyId,
				tag, fileLocation, conf.PermissionObjectDefault, "",
				conf.ObjectTypeNormal, "", "", crcChecksum,
				time.Now().Format(time.RFC3339), fileSize)

			f.Close()
			os.Remove(bucketName + "_log.log")
			os.Create(bucketName + "_log.log")
		}()
	}
}
